﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using ProtoBuf;
using ProtoBuf.Meta;
using HiTimer;
using Engine;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using SharpDX.Direct3D;
using SharpDX.DXGI;
using SharpDX.Windows;
using SharpDX.DirectInput;
using SharpDX.Multimedia;
using SharpDX.RawInput;
using Device = SharpDX.Direct3D11.Device;
using D2D = SharpDX.Direct2D1;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;

namespace ModelViewer
{
    static class Program
    {
        public static HiResTimer HiTimer;
        static System.Int64 IntPreTime;
        static float TimeUpdate = 0;

        static float TimeGC = 0;

        public static DeviceContext context;
        public static SwapChain swapChain;
        public static Device device;
        public static RenderForm form;
        public static Viewport viewport;

        public static Scene MyScene = new Scene();
        public static FeatureLevel featureLevel;
        /// <summary>
        /// Главная точка входа для приложения.
        /// </summary>
        [STAThread]
        static void Main()
        {
            #region Предварительная настройка
            RuntimeTypeModel.Default.Add(typeof(Vector3), false).Add("X", "Y", "Z");
            RuntimeTypeModel.Default.Add(typeof(Matrix), false).Add("M11", "M12", "M13", "M14",
                                                                    "M21", "M22", "M23", "M24",
                                                                    "M31", "M32", "M33", "M34",
                                                                    "M41", "M42", "M43", "M44");
            RuntimeTypeModel.Default.Add(typeof(Quaternion), false).Add("X", "Y", "Z", "W");
            RuntimeTypeModel.Default.Add(typeof(DVector3), false).Add("X", "Y", "Z");
            RuntimeTypeModel.Default.Add(typeof(DMatrix), false).Add("M11", "M12", "M13", "M14",
                                                                     "M21", "M22", "M23", "M24",
                                                                     "M31", "M32", "M33", "M34",
                                                                     "M41", "M42", "M43", "M44");
            RuntimeTypeModel.Default.Add(typeof(DQuaternion), false).Add("X", "Y", "Z", "W");
            Process thisProc = Process.GetCurrentProcess();
            thisProc.PriorityClass = ProcessPriorityClass.High;
            #endregion

            using (StartForm Input_form = new StartForm())
            {
                Application.Run(Input_form);
            }

            featureLevel = Device.GetSupportedFeatureLevel();
            //featureLevel = FeatureLevel.Level_10_0;


            if (Global.Settings.Run && featureLevel >= FeatureLevel.Level_10_0)
            {
                HiTimer = new HiResTimer();
                IntPreTime = HiTimer.Value;


                form = new RenderForm()
                {
                    ClientSize = new System.Drawing.Size(Global.Settings.Width, Global.Settings.Height),
                    Text = "Model Viewer"
                };
                //form.FormBorderStyle = FormBorderStyle.None;
                form.StartPosition = FormStartPosition.CenterScreen;


                InputManager.Init();

                var description = new SwapChainDescription()
                {
                    BufferCount = 2,
                    Usage = Usage.RenderTargetOutput,
                    OutputHandle = form.Handle,
                    IsWindowed = true,
                    ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                    SampleDescription = new SampleDescription(1, 0), //8, 32
                    //Flags = SwapChainFlags.AllowModeSwitch,
                    SwapEffect = SwapEffect.Discard
                };

                FeatureLevel[] f_level = new FeatureLevel[] { (featureLevel > FeatureLevel.Level_10_0) ? FeatureLevel.Level_10_1 : FeatureLevel.Level_10_0 };
                Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, f_level, description, out device, out swapChain);
                // setting a viewport is required if you want to actually see anything
                context = device.ImmediateContext;
                viewport = new Viewport(0, 0, Global.Settings.Width, Global.Settings.Height);
                context.Rasterizer.SetViewports(viewport);

                // prevent DXGI handling of alt+enter, which doesn't work properly with Winforms

                var factory_ = swapChain.GetParent<Factory>();
                    factory_.MakeWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAll);
                
                // handle alt+enter ourselves
                form.KeyDown += FormOnKeyDown;
                // handle form size changes
                form.UserResized += Resize;

                ////Scene inizialize
                MyScene.Inizialize(device, viewport, description, context, swapChain);
                
                SharpDX.DXGI.Surface surface = Surface.FromSwapChain(swapChain, 0);
                
                DrawHelper.Init(device);
                DebugDraw.Init(device);
                QuadScreen.Init(device, (int)viewport.Width, (int)viewport.Height);

                MyScene.LoadContent(device);

                swapChain.IsFullScreen = Global.Settings.FullScr;

               // RenderLoop.UseCustomDoEvents = false;
                RenderLoop.Run(form, () =>
                {
                    //RenderLoop.UseCustomDoEvents = false;
                    //Application.DoEvents();
                    TimeUpdate = HiTimer.Tick2Mili(HiTimer.Value - IntPreTime);
                    IntPreTime = HiTimer.Value;

                    InputManager.Update(TimeUpdate);

                    MyScene.Update(TimeUpdate);

                    MyScene.Draw(TimeUpdate, device, context);

                    swapChain.Present(Global.Settings.VSync, PresentFlags.None);
                    
                    TimeGC += TimeUpdate;
                    if (TimeGC > 300)
                    {
                        TimeGC = 0;
                        System.GC.Collect(1);
                    }

                });

                
                if (swapChain.IsFullScreen) swapChain.IsFullScreen = false;
                //MyScene.Dispose();
                
                DrawHelper.FreeResource();
                ContentManager.FreeResources();
                InputManager.FreeResource();
                DebugDraw.FreeResource();
                QuadScreen.FreeResource();
                factory_.Dispose();
                swapChain.Dispose();
                device.Dispose();
                context.Dispose();
            }
        }
        private static void FormOnKeyDown(object o, KeyEventArgs e)
        {
            if (e.Alt && e.KeyCode == Keys.Enter)
                swapChain.IsFullScreen = !swapChain.IsFullScreen;
            if (e.KeyCode == Keys.Escape) form.Close();
        }

        private static void Resize(object o, System.EventArgs e)
        {
            //TODO ?
            viewport = new Viewport(0, 0, Global.Settings.Width, Global.Settings.Height);
            //context.OutputMerger.SetTargets(renderTarget);
            context.Rasterizer.SetViewports(viewport);
            //Scene.Resize(device, swapChain, context);
        }

    }
}
